package org.gdstash.db;

import org.gdstash.db.criteria.*;
import org.gdstash.file.GDReader;
import org.gdstash.file.GDWriter;
import org.gdstash.item.GDItem;
import org.gdstash.ui.GDStashFrame;
import org.gdstash.util.FileVersionException;
import org.gdstash.util.GDLog;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.LinkedList;
import java.util.List;

public class DBStashItem implements Cloneable, ParameterSet {
    public static final String TABLE_NAME = "STASH_ITEM_V10";
    public static final String TABLE_NAME_V1 = "STASH_ITEM";
    public static final String TABLE_NAME_V2 = "STASH_ITEM_V2";
    public static final String TABLE_NAME_V3 = "STASH_ITEM_V3";
    public static final String TABLE_NAME_V4 = "STASH_ITEM_V4";
    public static final String TABLE_NAME_V5 = "STASH_ITEM_V5";
    public static final String TABLE_NAME_V6 = "STASH_ITEM_V6";
    public static final String TABLE_NAME_V7 = "STASH_ITEM_V7";
    public static final String TABLE_NAME_V8 = "STASH_ITEM_V8";
    public static final String TABLE_NAME_V9 = "STASH_ITEM_V9";
    public static final String TABLE_NAME_V10 = "STASH_ITEM_V10";
    public static final int VERSION_STASH_FILE = 1;
    public static final int VERSION_STASH_FILE_FOA = 2;
    private static final int ROW_STASH_ID = 1;
    private static final int ROW_ITEM_ID = 2;
    private static final int ROW_PREFIX_ID = 3;
    private static final int ROW_SUFFIX_ID = 4;
    private static final int ROW_MODIFIER_ID = 5;
    private static final int ROW_TRANSMUTE_ID = 6;
    private static final int ROW_ITEM_SEED = 7;
    private static final int ROW_RELIC_ID = 8;
    private static final int ROW_RELICBONUS_ID = 9;
    private static final int ROW_RELIC_SEED = 10;
    private static final int ROW_ENCHANTMENT_ID = 11;
    private static final int ROW_ENCHANTMENT_LEVEL = 12;
    private static final int ROW_ENCHANTMENT_SEED = 13;
    private static final int ROW_ITEM_VAR1 = 14;
    private static final int ROW_STACKCOUNT = 15;
    private static final int ROW_HARDCORE = 16;
    private static final int ROW_CHARNAME = 17;
    private static final int ROW_FOA_STASH_ID = 1;
    private static final int ROW_FOA_ITEM_ID = 2;
    private static final int ROW_FOA_PREFIX_ID = 3;
    private static final int ROW_FOA_SUFFIX_ID = 4;
    private static final int ROW_FOA_MODIFIER_ID = 5;
    private static final int ROW_FOA_TRANSMUTE_ID = 6;
    private static final int ROW_FOA_ITEM_SEED = 7;
    private static final int ROW_FOA_RELIC_ID = 8;
    private static final int ROW_FOA_RELICBONUS_ID = 9;
    private static final int ROW_FOA_RELIC_SEED = 10;
    private static final int ROW_FOA_ENCHANTMENT_ID = 11;
    private static final int ROW_FOA_ENCHANTMENT_LEVEL = 12;
    private static final int ROW_FOA_ENCHANTMENT_SEED = 13;
    private static final int ROW_FOA_ASCENDANT_ID = 14;
    private static final int ROW_FOA_ASCENDANT_2H_ID = 15;
    private static final int ROW_FOA_ITEM_VAR1 = 16;
    private static final int ROW_FOA_STACKCOUNT = 17;
    private static final int ROW_FOA_REROLLS_USED = 18;
    private static final int ROW_FOA_HARDCORE = 19;
    private static final int ROW_FOA_CHARNAME = 20;
    private static final int ROW_SOULBOUND = 21;
    private static final int ROW_RARITY = 22;
    private static final int ROW_REQ_LEVEL = 23;
    private static final int ROW_REQ_DEX = 24;
    private static final int ROW_REQ_INT = 25;
    private static final int ROW_REQ_STR = 26;
    private static final int ROW_ITEM_CLASS = 27;
    private static final int ROW_ARMOR_CLASS = 28;
    private static final int ROW_ARTIFACT_CLASS = 29;
    private static final int ROW_ITEM_LEVEL = 30;
    private static final int ROW_SET_ID = 31;
    private static final int ROW_ITEM_NAME = 32;
    private static final int ROW_PET_BONUS_SKILL_ID = 33;
    private static final int ROW_PLUS_ALLSKILLS = 34;
    private static final int ROW_ITEM_SKILL_ID = 35;
    private static final int ROW_CONVERT_IN = 36;
    private static final int ROW_CONVERT_OUT = 37;
    private static final int ROW_CONVERT_IN_2 = 38;
    private static final int ROW_CONVERT_OUT_2 = 39;
    private static final int ROW_ENEMY_ONLY = 40;
    private static final int ROW_SLOT_AXE_1H = 41;
    private static final int ROW_SLOT_AXE_2H = 42;
    private static final int ROW_SLOT_DAGGER_1H = 43;
    private static final int ROW_SLOT_MACE_1H = 44;
    private static final int ROW_SLOT_MACE_2H = 45;
    private static final int ROW_SLOT_SCEPTER_1H = 46;
    private static final int ROW_SLOT_SPEAR_1H = 47;
    private static final int ROW_SLOT_SPEAR_2H = 48;
    private static final int ROW_SLOT_STAFF_2H = 49;
    private static final int ROW_SLOT_SWORD_1H = 50;
    private static final int ROW_SLOT_SWORD_2H = 51;
    private static final int ROW_SLOT_RANGED_1H = 52;
    private static final int ROW_SLOT_RANGED_2H = 53;
    private static final int ROW_SLOT_SHIELD = 54;
    private static final int ROW_SLOT_OFFHAND = 55;
    private static final int ROW_SLOT_AMULET = 56;
    private static final int ROW_SLOT_BELT = 57;
    private static final int ROW_SLOT_MEDAL = 58;
    private static final int ROW_SLOT_RING = 59;
    private static final int ROW_SLOT_HEAD = 60;
    private static final int ROW_SLOT_SHOULDERS = 61;
    private static final int ROW_SLOT_CHEST = 62;
    private static final int ROW_SLOT_HANDS = 63;
    private static final int ROW_SLOT_LEGS = 64;
    private static final int ROW_SLOT_FEET = 65;
    private int stashID;
    private String itemID;
    private String prefixID;
    private String suffixID;
    private String modifierID;
    private String transmuteID;
    private int seed;
    private String relicID;
    private String relicBonusID;
    private int relicSeed;
    private String enchantmentID;
    private int enchantmentLevel;
    private int enchantmentSeed;
    private String ascendantID;
    private String ascendant2hID;
    private int var1;
    private int stackCount;
    private int rerollsUsed;
    private boolean hardcore;
    private String charname;
    private boolean soulbound;
    private String rarity;
    private int reqLevel;
    private int reqDex;
    private int reqInt;
    private int reqStr;
    private String itemClass;
    private String armorClass;
    private String artifactClass;
    private int itemLevel;
    private String setID;
    private String itemName;
    private String petBonusSkillID;
    private int plusAllSkills;
    private String itemSkillID;
    private String convertIn;
    private String convertOut;
    private String convertIn2;
    private String convertOut2;
    private boolean enemyOnly;
    private ItemSlots slots;
    private DBItem dbItem;
    private DBAffix dbPrefix;
    private DBAffix dbSuffix;
    private DBAffix dbModifier;
    private DBItem dbComponent;
    private DBAffix dbBonus;
    private DBItem dbEnchantment;

    public DBStashItem() {
        this.stashID = -1;
        this.itemID = null;
        this.prefixID = null;
        this.suffixID = null;
        this.modifierID = null;
        this.transmuteID = null;
        this.seed = 0;
        this.relicID = null;
        this.relicBonusID = null;
        this.relicSeed = 0;
        this.enchantmentID = null;
        this.enchantmentLevel = 0;
        this.enchantmentSeed = 0;
        this.ascendantID = null;
        this.ascendant2hID = null;
        this.var1 = 0;
        this.stackCount = 0;
        this.rerollsUsed = 0;
        this.hardcore = false;
        this.charname = null;
        this.slots = new ItemSlots();
        this.fillDependentStats(null);
    }

    public DBStashItem(final String charname, final boolean hardcore) {
        this();
        this.charname = charname;
        this.hardcore = hardcore;
    }

    public DBStashItem(final DBItem item) {
        this();
        if (item != null) {
            this.itemID = item.getItemID();
            if (item.isComponent()) {
                this.var1 = item.getComponentPieces();
            }
        }
        this.fillDependentStats(null);
    }

    public DBStashItem(final DBStashItem item) {
        this.stashID = -1;
        this.itemID = item.itemID;
        this.prefixID = item.prefixID;
        this.suffixID = item.suffixID;
        this.modifierID = item.modifierID;
        this.transmuteID = item.transmuteID;
        this.seed = item.seed;
        this.relicID = item.relicID;
        this.relicBonusID = item.relicBonusID;
        this.relicSeed = item.relicSeed;
        this.enchantmentID = item.enchantmentID;
        this.enchantmentLevel = item.enchantmentLevel;
        this.enchantmentSeed = item.enchantmentSeed;
        this.ascendantID = item.ascendantID;
        this.ascendant2hID = item.ascendant2hID;
        this.var1 = item.var1;
        this.stackCount = item.stackCount;
        this.rerollsUsed = item.rerollsUsed;
        this.hardcore = item.hardcore;
        this.charname = item.charname;
        this.slots = item.slots.clone();
        this.fillDependentStats(null);
    }

    public static void createTable() throws SQLException {
        final String dropTable = "DROP TABLE STASH_ITEM_V10";
        final String createTable = "CREATE TABLE STASH_ITEM_V10 (STASH_ID             INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),ITEM_ID              VARCHAR(256) NOT NULL, PREFIX_ID            VARCHAR(256), SUFFIX_ID            VARCHAR(256), MODIFIER_ID          VARCHAR(256), TRANSMUTE_ID         VARCHAR(256), ITEM_SEED            INTEGER, RELIC_ID             VARCHAR(256), RELICBONUS_ID        VARCHAR(256), RELIC_SEED           INTEGER, ENCHANTMENT_ID       VARCHAR(256), ENCHANTMENT_LEVEL    INTEGER, ENCHANTMENT_SEED     INTEGER, ASCENDANT_ID         VARCHAR(256), ASCENDANT_2H_ID      VARCHAR(256), ITEM_VAR1            INTEGER, STACK_COUNT          INTEGER, REROLLS_USED         INTEGER, HARDCORE             BOOLEAN, CHARNAME             VARCHAR(64), SOULBOUND            BOOLEAN, RARITY               VARCHAR(32), REQ_LEVEL            INTEGER, REQ_DEX              INTEGER, REQ_INT              INTEGER, REQ_STR              INTEGER, ITEM_CLASS           VARCHAR(32), ARMOR_CLASS          VARCHAR(32), ARTIFACT_CLASS       VARCHAR(32), ITEM_LEVEL           INTEGER, SET_ID               VARCHAR(256), NAME                 VARCHAR(256), PET_BONUS_SKILL_ID   VARCHAR(256), PLUS_ALLSKILLS       INTEGER, ITEM_SKILL_ID        VARCHAR(256), COMPONENT_SKILL_ID   VARCHAR(256), ENCHANTMENT_SKILL_ID VARCHAR(256), CONVERT_IN           VARCHAR(16), CONVERT_OUT          VARCHAR(16), CONVERT_IN_2         VARCHAR(16), CONVERT_OUT_2        VARCHAR(16), ENEMY_ONLY           BOOLEAN, SLOT_AXE_1H          BOOLEAN, SLOT_AXE_2H          BOOLEAN, SLOT_DAGGER_1H       BOOLEAN, SLOT_MACE_1H         BOOLEAN, SLOT_MACE_2H         BOOLEAN, SLOT_SCEPTER_1H      BOOLEAN, SLOT_SPEAR_1H        BOOLEAN, SLOT_SPEAR_2H        BOOLEAN, SLOT_STAFF_2H        BOOLEAN, SLOT_SWORD_1H        BOOLEAN, SLOT_SWORD_2H        BOOLEAN, SLOT_RANGED_1H       BOOLEAN, SLOT_RANGED_2H       BOOLEAN, SLOT_SHIELD          BOOLEAN, SLOT_OFFHAND         BOOLEAN, SLOT_AMULET          BOOLEAN, SLOT_BELT            BOOLEAN, SLOT_MEDAL           BOOLEAN, SLOT_RING            BOOLEAN, SLOT_HEAD            BOOLEAN, SLOT_SHOULDERS       BOOLEAN, SLOT_CHEST           BOOLEAN, SLOT_HANDS           BOOLEAN, SLOT_LEGS            BOOLEAN, SLOT_FEET            BOOLEAN, PRIMARY KEY (STASH_ID))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "STASH_ITEM_V10")) {
                    st.execute(dropTable);
                }
                st.execute(createTable);
                st.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"STASH_ITEM_V10"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static void storeDBItems(final List<DBStashItem> items) {
        if (items == null) {
            return;
        }
        int iFound = 0;
        int iInsert = 0;
        for (final DBStashItem item : items) {
            boolean found = false;
            try {
                found = isStored(item);
            } catch (SQLException ex) {
                final Object[] args = {item.itemID};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_SEARCH", args);
                GDMsgLogger.addError(msg);
                GDMsgLogger.addError(ex);
                continue;
            }
            if (found) {
                ++iFound;
            } else {
                try {
                    insert(item);
                    ++iInsert;
                } catch (SQLException ex) {
                    final Object[] args = {item.itemID};
                    final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_STORE", args);
                    GDMsgLogger.addError(msg);
                    GDMsgLogger.addError(ex);
                }
            }
        }
        final Object[] args2 = {iInsert};
        String msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_STORED_NUM", args2);
        GDMsgLogger.addError(msg2);
        final Object[] args3 = {iFound};
        msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_FOUND_NUM", args3);
        GDMsgLogger.addError(msg2);
    }

    private static void updateStackCount(final Connection conn, final int stashID, final int stackSize) throws SQLException {
        final String update = "UPDATE STASH_ITEM_V10 SET STACK_COUNT = ? WHERE STASH_ID = ?";
        try (final PreparedStatement ps = conn.prepareStatement(update)) {
            ps.setInt(1, stackSize);
            ps.setInt(2, stashID);
            ps.executeUpdate();
            ps.close();
        } catch (SQLException ex) {
            throw ex;
        }
    }

    private static void insertNonStack(final Connection conn, final DBStashItem item) throws SQLException {
        final String insert = "INSERT INTO STASH_ITEM_V10(ITEM_ID, PREFIX_ID, SUFFIX_ID, MODIFIER_ID, TRANSMUTE_ID, ITEM_SEED, RELIC_ID, RELICBONUS_ID, RELIC_SEED, ENCHANTMENT_ID, ENCHANTMENT_LEVEL, ENCHANTMENT_SEED, ASCENDANT_ID, ASCENDANT_2H_ID, ITEM_VAR1, STACK_COUNT, REROLLS_USED, HARDCORE, CHARNAME, SOULBOUND, RARITY, REQ_LEVEL, REQ_DEX, REQ_INT, REQ_STR, ITEM_CLASS, ARMOR_CLASS, ARTIFACT_CLASS, ITEM_LEVEL, SET_ID, NAME, PET_BONUS_SKILL_ID, PLUS_ALLSKILLS, ITEM_SKILL_ID, CONVERT_IN, CONVERT_OUT, CONVERT_IN_2, CONVERT_OUT_2, ENEMY_ONLY, SLOT_AXE_1H, SLOT_AXE_2H, SLOT_DAGGER_1H, SLOT_MACE_1H, SLOT_MACE_2H, SLOT_SCEPTER_1H, SLOT_SPEAR_1H, SLOT_SPEAR_2H, SLOT_STAFF_2H, SLOT_SWORD_1H, SLOT_SWORD_2H, SLOT_RANGED_1H, SLOT_RANGED_2H, SLOT_SHIELD, SLOT_OFFHAND, SLOT_AMULET, SLOT_BELT, SLOT_MEDAL, SLOT_RING, SLOT_HEAD, SLOT_SHOULDERS, SLOT_CHEST, SLOT_HANDS, SLOT_LEGS, SLOT_FEET) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
        try (final PreparedStatement ps = conn.prepareStatement(insert)) {
            ps.setString(1, item.itemID);
            if (item.prefixID == null) {
                ps.setString(2, "");
            } else {
                ps.setString(2, item.prefixID);
            }
            if (item.suffixID == null) {
                ps.setString(3, "");
            } else {
                ps.setString(3, item.suffixID);
            }
            if (item.modifierID == null) {
                ps.setString(4, "");
            } else {
                ps.setString(4, item.modifierID);
            }
            if (item.transmuteID == null) {
                ps.setString(5, "");
            } else {
                ps.setString(5, item.transmuteID);
            }
            ps.setInt(6, item.seed);
            if (item.relicID == null) {
                ps.setString(7, "");
            } else {
                ps.setString(7, item.relicID);
            }
            if (item.relicBonusID == null) {
                ps.setString(8, "");
            } else {
                ps.setString(8, item.relicBonusID);
            }
            ps.setInt(9, item.relicSeed);
            if (item.enchantmentID == null) {
                ps.setString(10, "");
            } else {
                ps.setString(10, item.enchantmentID);
            }
            ps.setInt(11, item.enchantmentLevel);
            ps.setInt(12, item.enchantmentSeed);
            if (item.ascendantID == null) {
                ps.setString(13, "");
            } else {
                ps.setString(13, item.ascendantID);
            }
            if (item.ascendant2hID == null) {
                ps.setString(14, "");
            } else {
                ps.setString(14, item.ascendant2hID);
            }
            ps.setInt(15, item.var1);
            ps.setInt(16, item.stackCount);
            ps.setInt(17, item.rerollsUsed);
            ps.setBoolean(18, item.hardcore);
            if (item.charname == null) {
                ps.setString(19, "");
            } else {
                ps.setString(19, item.charname);
            }
            ps.setBoolean(20, item.soulbound);
            ps.setString(21, item.rarity);
            ps.setInt(22, item.reqLevel);
            ps.setInt(23, item.reqDex);
            ps.setInt(24, item.reqInt);
            ps.setInt(25, item.reqStr);
            ps.setString(26, item.itemClass);
            ps.setString(27, item.armorClass);
            ps.setString(28, item.artifactClass);
            ps.setInt(29, item.itemLevel);
            ps.setString(30, item.setID);
            ps.setString(31, item.itemName);
            ps.setString(32, item.petBonusSkillID);
            ps.setInt(33, item.plusAllSkills);
            ps.setString(34, item.itemSkillID);
            ps.setString(35, item.convertIn);
            ps.setString(36, item.convertOut);
            ps.setString(37, item.convertIn2);
            ps.setString(38, item.convertOut2);
            ps.setBoolean(39, item.enemyOnly);
            ps.setBoolean(40, item.slots.slotAxe1H);
            ps.setBoolean(41, item.slots.slotAxe2H);
            ps.setBoolean(42, item.slots.slotDagger1H);
            ps.setBoolean(43, item.slots.slotMace1H);
            ps.setBoolean(44, item.slots.slotMace2H);
            ps.setBoolean(45, item.slots.slotScepter1H);
            ps.setBoolean(46, item.slots.slotSpear1H);
            ps.setBoolean(47, item.slots.slotSpear2H);
            ps.setBoolean(48, item.slots.slotStaff2H);
            ps.setBoolean(49, item.slots.slotSword1H);
            ps.setBoolean(50, item.slots.slotSword2H);
            ps.setBoolean(51, item.slots.slotRanged1H);
            ps.setBoolean(52, item.slots.slotRanged2H);
            ps.setBoolean(53, item.slots.slotShield);
            ps.setBoolean(54, item.slots.slotOffhand);
            ps.setBoolean(55, item.slots.slotAmulet);
            ps.setBoolean(56, item.slots.slotBelt);
            ps.setBoolean(57, item.slots.slotMedal);
            ps.setBoolean(58, item.slots.slotRing);
            ps.setBoolean(59, item.slots.slotHead);
            ps.setBoolean(60, item.slots.slotShoulders);
            ps.setBoolean(61, item.slots.slotChest);
            ps.setBoolean(62, item.slots.slotHands);
            ps.setBoolean(63, item.slots.slotLegs);
            ps.setBoolean(64, item.slots.slotFeet);
            ps.executeUpdate();
            ps.close();
        } catch (SQLException ex) {
            throw ex;
        }
    }

    private static DBStashItem getStashItemForStack(final DBStashItem item) throws SQLException {
        DBStashItem si = null;
        final String command = "SELECT * FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND ITEM_VAR1 = ? AND HARDCORE = ? AND CHARNAME = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, item.itemID);
            ps.setInt(2, item.var1);
            ps.setBoolean(3, item.hardcore);
            if (item.charname == null) {
                ps.setString(4, "");
            } else {
                ps.setString(4, item.charname);
            }
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBStashItem> list = wrap(rs);
                if (list.isEmpty()) {
                    si = null;
                } else {
                    si = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                si = null;
                throw ex;
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
        return si;
    }

    private static int getStashIDForStack(final DBStashItem item) throws SQLException {
        int id = -1;
        final String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND HARDCORE = ? AND CHARNAME = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, item.itemID);
            ps.setBoolean(2, item.hardcore);
            if (item.charname == null) {
                ps.setString(3, "");
            } else {
                ps.setString(3, item.charname);
            }
            try (final ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    id = rs.getInt(1);
                }
                conn.commit();
            } catch (SQLException ex) {
                id = -1;
                throw ex;
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
        return id;
    }

    private static boolean isStoredStack(final DBStashItem item) throws SQLException {
        boolean found = false;
        final String command = "SELECT * FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND HARDCORE = ? AND CHARNAME = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, item.itemID);
            ps.setBoolean(2, item.hardcore);
            if (item.charname == null) {
                ps.setString(3, "");
            } else {
                ps.setString(3, item.charname);
            }
            try (final ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    found = true;
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
        return found;
    }

    private static boolean isStoredNonStack(final DBStashItem item) throws SQLException {
        boolean found = false;
        final String command = "SELECT * FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND PREFIX_ID = ? AND SUFFIX_ID = ? AND MODIFIER_ID = ? AND TRANSMUTE_ID = ? AND ITEM_SEED = ? AND RELIC_ID = ? AND RELICBONUS_ID = ? AND RELIC_SEED = ? AND ENCHANTMENT_ID =? AND ENCHANTMENT_LEVEL = ? AND ENCHANTMENT_SEED = ? AND ASCENDANT_ID = ? AND ASCENDANT_2H_ID = ? AND ITEM_VAR1 = ? AND STACK_COUNT = ? AND HARDCORE = ? AND CHARNAME = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, item.itemID);
            if (item.prefixID == null) {
                ps.setString(2, "");
            } else {
                ps.setString(2, item.prefixID);
            }
            if (item.suffixID == null) {
                ps.setString(3, "");
            } else {
                ps.setString(3, item.suffixID);
            }
            if (item.modifierID == null) {
                ps.setString(4, "");
            } else {
                ps.setString(4, item.modifierID);
            }
            if (item.transmuteID == null) {
                ps.setString(5, "");
            } else {
                ps.setString(5, item.transmuteID);
            }
            ps.setInt(6, item.seed);
            if (item.relicID == null) {
                ps.setString(7, "");
            } else {
                ps.setString(7, item.relicID);
            }
            if (item.relicBonusID == null) {
                ps.setString(8, "");
            } else {
                ps.setString(8, item.relicBonusID);
            }
            ps.setInt(9, item.relicSeed);
            if (item.enchantmentID == null) {
                ps.setString(10, "");
            } else {
                ps.setString(10, item.enchantmentID);
            }
            ps.setInt(11, item.enchantmentLevel);
            ps.setInt(12, item.enchantmentSeed);
            if (item.ascendantID == null) {
                ps.setString(13, "");
            } else {
                ps.setString(13, item.ascendantID);
            }
            if (item.ascendant2hID == null) {
                ps.setString(14, "");
            } else {
                ps.setString(14, item.ascendant2hID);
            }
            ps.setInt(15, item.var1);
            ps.setInt(16, item.stackCount);
            ps.setBoolean(17, item.hardcore);
            if (item.charname == null) {
                ps.setString(18, "");
            } else {
                ps.setString(18, item.charname);
            }
            try (final ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    found = true;
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
        return found;
    }

    private static boolean deleteStackDB(final DBStashItem item) {
        if (item == null) {
            return false;
        }
        boolean success = false;
        final String command = "DELETE FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND ITEM_VAR1 = ? AND HARDCORE = ? AND CHARNAME = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(command)) {
                ps.setString(1, item.itemID);
                ps.setInt(2, item.var1);
                ps.setBoolean(3, item.hardcore);
                if (item.charname == null) {
                    ps.setString(4, "");
                } else {
                    ps.setString(4, item.charname);
                }
                ps.executeUpdate();
                ps.close();
                conn.commit();
                success = true;
            } catch (SQLException ex) {
                conn.rollback();
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            final Object[] args = {item.itemID};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_DELETE", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return success;
    }

    private static boolean deleteNonStack(final DBStashItem item) {
        if (item == null) {
            return false;
        }
        boolean success = false;
        final String command = "DELETE FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND PREFIX_ID = ? AND SUFFIX_ID = ? AND MODIFIER_ID = ? AND TRANSMUTE_ID = ? AND ITEM_SEED = ? AND RELIC_ID = ? AND RELICBONUS_ID = ? AND RELIC_SEED = ? AND ENCHANTMENT_ID = ? AND ENCHANTMENT_LEVEL = ? AND ENCHANTMENT_SEED = ? AND ASCENDANT_ID = ? AND ASCENDANT_2H_ID = ? AND ITEM_VAR1 = ? AND STACK_COUNT = ? AND REROLLS_USED = ? AND HARDCORE = ? AND CHARNAME = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(command)) {
                ps.setString(1, item.itemID);
                if (item.prefixID == null) {
                    ps.setString(2, "");
                } else {
                    ps.setString(2, item.prefixID);
                }
                if (item.suffixID == null) {
                    ps.setString(3, "");
                } else {
                    ps.setString(3, item.suffixID);
                }
                if (item.modifierID == null) {
                    ps.setString(4, "");
                } else {
                    ps.setString(4, item.modifierID);
                }
                if (item.transmuteID == null) {
                    ps.setString(5, "");
                } else {
                    ps.setString(5, item.transmuteID);
                }
                ps.setInt(6, item.seed);
                if (item.relicID == null) {
                    ps.setString(7, "");
                } else {
                    ps.setString(7, item.relicID);
                }
                if (item.relicBonusID == null) {
                    ps.setString(8, "");
                } else {
                    ps.setString(8, item.relicBonusID);
                }
                ps.setInt(9, item.relicSeed);
                if (item.enchantmentID == null) {
                    ps.setString(10, "");
                } else {
                    ps.setString(10, item.enchantmentID);
                }
                ps.setInt(11, item.enchantmentLevel);
                ps.setInt(12, item.enchantmentSeed);
                if (item.ascendantID == null) {
                    ps.setString(13, "");
                } else {
                    ps.setString(13, item.ascendantID);
                }
                if (item.ascendant2hID == null) {
                    ps.setString(14, "");
                } else {
                    ps.setString(14, item.ascendant2hID);
                }
                ps.setInt(15, item.var1);
                ps.setInt(16, item.stackCount);
                ps.setInt(17, item.rerollsUsed);
                ps.setBoolean(18, item.hardcore);
                if (item.charname == null) {
                    ps.setString(19, "");
                } else {
                    ps.setString(19, item.charname);
                }
                ps.executeUpdate();
                ps.close();
                conn.commit();
                success = true;
            } catch (SQLException ex) {
                conn.rollback();
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            final Object[] args = {item.itemID};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_DELETE", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return success;
    }

    private static void deleteItemByStashID(final DBStashItem item) throws SQLException {
        if (item == null) {
            return;
        }
        if (item.stashID == -1) {
            return;
        }
        final String command = "DELETE FROM STASH_ITEM_V10 WHERE STASH_ID = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(command)) {
                ps.setInt(1, item.stashID);
                ps.executeUpdate();
                ps.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    private static void updateDependentFields(final DBStashItem item) throws SQLException {
        final String update = "UPDATE STASH_ITEM_V10 SET PREFIX_ID = ?, SUFFIX_ID = ?, MODIFIER_ID = ?, TRANSMUTE_ID = ?, ITEM_SEED = ?, RELIC_ID = ?, RELICBONUS_ID = ?, RELIC_SEED = ?, ENCHANTMENT_ID = ?, ENCHANTMENT_LEVEL = ?, ENCHANTMENT_SEED = ?, ASCENDANT_ID = ?, ASCENDANT_2H_ID = ?, ITEM_VAR1 = ?, STACK_COUNT = ?, REROLLS_USED = ?, HARDCORE = ?, CHARNAME = ?, SOULBOUND = ?, RARITY = ?,  REQ_LEVEL = ?, REQ_DEX = ?, REQ_INT = ?, REQ_STR = ?, ITEM_CLASS = ?, ARMOR_CLASS = ?, ARTIFACT_CLASS = ?, ITEM_LEVEL = ?, SET_ID = ?, NAME = ?, PET_BONUS_SKILL_ID = ?, PLUS_ALLSKILLS = ?, ITEM_SKILL_ID = ?, CONVERT_IN = ?, CONVERT_OUT = ?, CONVERT_IN_2 = ?, CONVERT_OUT_2 = ?, ENEMY_ONLY = ?, SLOT_AXE_1H = ?, SLOT_AXE_2H = ?, SLOT_DAGGER_1H = ?, SLOT_MACE_1H = ?, SLOT_MACE_2H = ?, SLOT_SCEPTER_1H = ?, SLOT_SPEAR_1H = ?, SLOT_SPEAR_2H = ?, SLOT_STAFF_2H = ?, SLOT_SWORD_1H = ?, SLOT_SWORD_2H = ?, SLOT_RANGED_1H = ?, SLOT_RANGED_2H = ?, SLOT_SHIELD = ?, SLOT_OFFHAND = ?, SLOT_AMULET = ?, SLOT_BELT = ?, SLOT_MEDAL = ?, SLOT_RING = ?, SLOT_HEAD = ?, SLOT_SHOULDERS = ?, SLOT_CHEST = ?, SLOT_HANDS = ?, SLOT_LEGS = ?, SLOT_FEET = ? WHERE STASH_ID = ?";
        item.fillDependentStats(null);
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(update)) {
                if (item.prefixID == null) {
                    ps.setString(1, "");
                } else {
                    ps.setString(1, item.prefixID);
                }
                if (item.suffixID == null) {
                    ps.setString(2, "");
                } else {
                    ps.setString(2, item.suffixID);
                }
                if (item.modifierID == null) {
                    ps.setString(3, "");
                } else {
                    ps.setString(3, item.modifierID);
                }
                if (item.transmuteID == null) {
                    ps.setString(4, "");
                } else {
                    ps.setString(4, item.transmuteID);
                }
                ps.setInt(5, item.seed);
                if (item.relicID == null) {
                    ps.setString(6, "");
                } else {
                    ps.setString(6, item.relicID);
                }
                if (item.relicBonusID == null) {
                    ps.setString(7, "");
                } else {
                    ps.setString(7, item.relicBonusID);
                }
                ps.setInt(8, item.relicSeed);
                if (item.enchantmentID == null) {
                    ps.setString(9, "");
                } else {
                    ps.setString(9, item.enchantmentID);
                }
                ps.setInt(10, item.enchantmentLevel);
                ps.setInt(11, item.enchantmentSeed);
                if (item.ascendantID == null) {
                    ps.setString(12, "");
                } else {
                    ps.setString(12, item.ascendantID);
                }
                if (item.ascendant2hID == null) {
                    ps.setString(13, "");
                } else {
                    ps.setString(13, item.ascendant2hID);
                }
                ps.setInt(14, item.var1);
                ps.setInt(15, item.stackCount);
                ps.setInt(16, item.rerollsUsed);
                ps.setBoolean(17, item.hardcore);
                if (item.charname == null) {
                    ps.setString(18, "");
                } else {
                    ps.setString(18, item.charname);
                }
                ps.setBoolean(19, item.soulbound);
                ps.setString(20, item.rarity);
                ps.setInt(21, item.reqLevel);
                ps.setInt(22, item.reqDex);
                ps.setInt(23, item.reqInt);
                ps.setInt(24, item.reqStr);
                ps.setString(25, item.itemClass);
                ps.setString(26, item.armorClass);
                ps.setString(27, item.artifactClass);
                ps.setInt(28, item.itemLevel);
                ps.setString(29, item.setID);
                ps.setString(30, item.itemName);
                ps.setString(31, item.petBonusSkillID);
                ps.setInt(32, item.plusAllSkills);
                ps.setString(33, item.itemSkillID);
                ps.setString(34, item.convertIn);
                ps.setString(35, item.convertOut);
                ps.setString(36, item.convertIn2);
                ps.setString(37, item.convertOut2);
                ps.setBoolean(38, item.enemyOnly);
                ps.setBoolean(39, item.slots.slotAxe1H);
                ps.setBoolean(40, item.slots.slotAxe2H);
                ps.setBoolean(41, item.slots.slotDagger1H);
                ps.setBoolean(42, item.slots.slotMace1H);
                ps.setBoolean(43, item.slots.slotMace2H);
                ps.setBoolean(44, item.slots.slotScepter1H);
                ps.setBoolean(45, item.slots.slotSpear1H);
                ps.setBoolean(46, item.slots.slotSpear2H);
                ps.setBoolean(47, item.slots.slotStaff2H);
                ps.setBoolean(48, item.slots.slotSword1H);
                ps.setBoolean(49, item.slots.slotSword2H);
                ps.setBoolean(50, item.slots.slotRanged1H);
                ps.setBoolean(51, item.slots.slotRanged2H);
                ps.setBoolean(52, item.slots.slotShield);
                ps.setBoolean(53, item.slots.slotOffhand);
                ps.setBoolean(54, item.slots.slotAmulet);
                ps.setBoolean(55, item.slots.slotBelt);
                ps.setBoolean(56, item.slots.slotMedal);
                ps.setBoolean(57, item.slots.slotRing);
                ps.setBoolean(58, item.slots.slotHead);
                ps.setBoolean(59, item.slots.slotShoulders);
                ps.setBoolean(60, item.slots.slotChest);
                ps.setBoolean(61, item.slots.slotHands);
                ps.setBoolean(62, item.slots.slotLegs);
                ps.setBoolean(63, item.slots.slotFeet);
                ps.setInt(64, item.stashID);
                ps.executeUpdate();
                ps.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    private static DBStashItem getStack(DBStashItem item) {
        if (item == null) {
            return null;
        }
        final String command = "SELECT * FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND ITEM_VAR1 = ? AND HARDCORE = ? AND CHARNAME = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, item.itemID);
            ps.setInt(2, item.var1);
            ps.setBoolean(3, item.hardcore);
            if (item.charname == null) {
                ps.setString(4, "");
            } else {
                ps.setString(4, item.charname);
            }
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBStashItem> list = wrap(rs);
                if (list.isEmpty()) {
                    item = null;
                } else {
                    item = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                item = null;
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {item.itemID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_READ_ALL"));
            GDMsgLogger.addError(ex2);
        }
        return item;
    }

    public static DBStashItem get(final int stashID, final CriteriaCombination.Soulbound soulbound, final CriteriaCombination.SC_HC schc, final boolean isHardcore, final String charName) {
        DBStashItem item = null;
        String command = "SELECT * FROM STASH_ITEM_V10 WHERE STASH_ID = ?";
        if (schc == CriteriaCombination.SC_HC.SOFTCORE) {
            command += " AND S.HARDCORE = false";
        }
        if (schc == CriteriaCombination.SC_HC.HARDCORE) {
            command += " AND S.HARDCORE = true";
        }
        if (schc == CriteriaCombination.SC_HC.SETTING && !GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (soulbound == CriteriaCombination.Soulbound.EXCLUDED) {
            command += " AND CHARNAME = ''";
        }
        if (soulbound == CriteriaCombination.Soulbound.SETTING && !GDStashFrame.iniConfig.sectRestrict.transferSoulbound) {
            if (charName == null) {
                command += " AND CHARNAME = ''";
            } else if (charName.isEmpty()) {
                command += " AND CHARNAME = ''";
            } else {
                command = command + " AND ( CHARNAME = '" + charName + "' OR CHARNAME = '' )";
            }
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setInt(1, stashID);
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBStashItem> list = wrap(rs);
                if (list.isEmpty()) {
                    item = null;
                } else {
                    item = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                item = null;
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {Integer.toString(stashID), "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_READ_ALL"));
            GDMsgLogger.addError(ex2);
        }
        return item;
    }

    public static List<DBStashItem> getAllOld(final Connection conn, final String tableName) {
        List<DBStashItem> list = new LinkedList<DBStashItem>();
        if (tableName.equals("STASH_ITEM_V10") || tableName.equals("STASH_ITEM_V2") || tableName.equals("STASH_ITEM_V3") || tableName.equals("STASH_ITEM_V4") || tableName.equals("STASH_ITEM_V5") || tableName.equals("STASH_ITEM_V6") || tableName.equals("STASH_ITEM_V7") || tableName.equals("STASH_ITEM_V8") || tableName.equals("STASH_ITEM_V9")) {
            list = getAllOld_V9(conn, tableName);
        }
        if (tableName.equals("STASH_ITEM_V10")) {
            list = getAllOld_V10(conn, tableName);
        }
        return list;
    }

    public static List<DBStashItem> getAllOld_V10(final Connection conn, final String tableName) {
        List<DBStashItem> list = new LinkedList<DBStashItem>();
        final String command = "SELECT * FROM " + tableName;
        try (final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID"));
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBStashItem> getAllOld_V9(final Connection conn, final String tableName) {
        List<DBStashItem> list = new LinkedList<DBStashItem>();
        final String command = "SELECT * FROM " + tableName;
        try (final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap_V9(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID"));
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBStashItem> getAll() {
        List<DBStashItem> list = new LinkedList<DBStashItem>();
        final String command = "SELECT * FROM STASH_ITEM_V10";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID"));
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBItem> getAllDistinct(final boolean hardcore) {
        final List<DBItem> list = new LinkedList<DBItem>();
        String command = "SELECT DISTINCT ITEM_ID FROM STASH_ITEM_V10";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (hardcore) {
                command += " WHERE HARDCORE = 'true'";
            } else {
                command += " WHERE HARDCORE = 'false'";
            }
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    final String itemID = rs.getString(1);
                    final DBItem item = DBItem.get(itemID);
                    list.add(item);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {"All", "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<GDItem> getGDItemsByItemID(final String itemID, final boolean isHardcore) {
        List<GDItem> list = new LinkedList<GDItem>();
        String command = "SELECT * FROM STASH_ITEM_V10 WHERE ITEM_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            command += " AND HARDCORE = ?";
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
                ps.setBoolean(2, isHardcore);
            }
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrapGDItem(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<GDItem> getGDItemsByItemID(final String itemID) {
        List<GDItem> list = new LinkedList<GDItem>();
        final String command = "SELECT * FROM STASH_ITEM_V10 WHERE ITEM_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrapGDItem(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<DBStashItem> wrap_V9(final ResultSet rs) throws SQLException {
        final LinkedList<DBStashItem> list = new LinkedList<DBStashItem>();
        while (rs.next()) {
            try {
                final DBStashItem item = wrapSingle_V9(rs);
                list.add(item);
            } catch (SQLException ex) {
            }
        }
        return list;
    }

    private static DBStashItem wrapSingle(final ResultSet rs) throws SQLException {
        final DBStashItem item = new DBStashItem();
        try {
            item.stashID = rs.getInt(1);
            item.itemID = rs.getString(2);
            item.prefixID = rs.getString(3);
            if (item.prefixID != null && item.prefixID.equals("")) {
                item.prefixID = null;
            }
            item.suffixID = rs.getString(4);
            if (item.suffixID != null && item.suffixID.equals("")) {
                item.suffixID = null;
            }
            item.modifierID = rs.getString(5);
            if (item.modifierID != null && item.modifierID.equals("")) {
                item.modifierID = null;
            }
            item.transmuteID = rs.getString(6);
            if (item.transmuteID != null && item.transmuteID.equals("")) {
                item.transmuteID = null;
            }
            item.seed = rs.getInt(7);
            item.relicID = rs.getString(8);
            if (item.relicID != null && item.relicID.equals("")) {
                item.relicID = null;
            }
            item.relicBonusID = rs.getString(9);
            if (item.relicBonusID != null && item.relicBonusID.equals("")) {
                item.relicBonusID = null;
            }
            item.relicSeed = rs.getInt(10);
            item.enchantmentID = rs.getString(11);
            if (item.enchantmentID != null && item.enchantmentID.equals("")) {
                item.enchantmentID = null;
            }
            item.enchantmentLevel = rs.getInt(12);
            item.enchantmentSeed = rs.getInt(13);
            item.ascendantID = rs.getString(14);
            if (item.ascendantID != null && item.ascendantID.equals("")) {
                item.ascendantID = null;
            }
            item.ascendant2hID = rs.getString(15);
            if (item.ascendant2hID != null && item.ascendant2hID.equals("")) {
                item.ascendant2hID = null;
            }
            item.var1 = rs.getInt(16);
            item.stackCount = rs.getInt(17);
            item.rerollsUsed = rs.getInt(18);
            item.hardcore = rs.getBoolean(19);
            item.charname = rs.getString(20);
            if (item.charname != null && item.charname.equals("")) {
                item.charname = null;
            }
            if (item.charname != null) {
                final ByteBuffer buffer = StandardCharsets.UTF_8.encode(item.charname);
                final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                item.charname = utf8;
            }
            item.fillDependentStats(null);
        } catch (SQLException ex) {
            if (item.itemID != null) {
                final Object[] args = {item.itemID, item.stashID};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_READ_BY_STASH", args);
                throw new ClassCastException(msg);
            }
            if (item.stashID != -1) {
                final Object[] args = {item.stashID, "STASH_ITEM_V10"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
                GDMsgLogger.addError(msg);
            } else {
                GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_STASH_READ"));
            }
            GDMsgLogger.addError(ex);
            throw ex;
        }
        return item;
    }

    private static DBStashItem wrapSingle_V9(final ResultSet rs) throws SQLException {
        final DBStashItem item = new DBStashItem();
        try {
            item.stashID = rs.getInt(1);
            item.itemID = rs.getString(2);
            item.prefixID = rs.getString(3);
            if (item.prefixID != null && item.prefixID.equals("")) {
                item.prefixID = null;
            }
            item.suffixID = rs.getString(4);
            if (item.suffixID != null && item.suffixID.equals("")) {
                item.suffixID = null;
            }
            item.modifierID = rs.getString(5);
            if (item.modifierID != null && item.modifierID.equals("")) {
                item.modifierID = null;
            }
            item.transmuteID = rs.getString(6);
            if (item.transmuteID != null && item.transmuteID.equals("")) {
                item.transmuteID = null;
            }
            item.seed = rs.getInt(7);
            item.relicID = rs.getString(8);
            if (item.relicID != null && item.relicID.equals("")) {
                item.relicID = null;
            }
            item.relicBonusID = rs.getString(9);
            if (item.relicBonusID != null && item.relicBonusID.equals("")) {
                item.relicBonusID = null;
            }
            item.relicSeed = rs.getInt(10);
            item.enchantmentID = rs.getString(11);
            if (item.enchantmentID != null && item.enchantmentID.equals("")) {
                item.enchantmentID = null;
            }
            item.enchantmentLevel = rs.getInt(12);
            item.enchantmentSeed = rs.getInt(13);
            item.ascendantID = null;
            item.ascendant2hID = null;
            item.var1 = rs.getInt(14);
            item.stackCount = rs.getInt(15);
            item.rerollsUsed = 0;
            item.hardcore = rs.getBoolean(16);
            item.charname = rs.getString(17);
            if (item.charname != null && item.charname.equals("")) {
                item.charname = null;
            }
            if (item.charname != null) {
                final ByteBuffer buffer = StandardCharsets.UTF_8.encode(item.charname);
                final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                item.charname = utf8;
            }
            item.fillDependentStats(null);
        } catch (SQLException ex) {
            if (item.itemID != null) {
                final Object[] args = {item.itemID, item.stashID};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_READ_BY_STASH", args);
                throw new ClassCastException(msg);
            }
            if (item.stashID != -1) {
                final Object[] args = {item.stashID, "STASH_ITEM_V10"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
                GDMsgLogger.addError(msg);
            } else {
                GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_STASH_READ"));
            }
            GDMsgLogger.addError(ex);
            throw ex;
        }
        return item;
    }

    private static List<Integer> getStashIDsByItemID(final String itemID, final boolean isHardcore, final String charName) {
        List<Integer> list = new LinkedList<Integer>();
        String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (!GDStashFrame.iniConfig.sectRestrict.transferSoulbound) {
            if (charName == null) {
                command += " AND CHARNAME = ''";
            } else if (charName.isEmpty()) {
                command += " AND CHARNAME = ''";
            } else {
                command = command + " AND ( CHARNAME = '" + charName + "' OR CHARNAME = '' )";
            }
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapInteger(rs, 1);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<Integer> getStashIDsByItemAndPrefixSuffix(final String itemID, final String prefixID, final String suffixID, final boolean isHardcore, final String charName) {
        List<Integer> list = new LinkedList<Integer>();
        if (prefixID == null && suffixID == null) {
            return list;
        }
        String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND PREFIX_ID = ? AND SUFFIX_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (!GDStashFrame.iniConfig.sectRestrict.transferSoulbound && charName != null) {
            command = command + " AND CHARNAME = '" + charName + "'";
        }
        String pre = "";
        if (prefixID != null) {
            pre = prefixID;
        }
        String suf = "";
        if (suffixID != null) {
            suf = suffixID;
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            ps.setString(2, pre);
            ps.setString(3, suf);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapInteger(rs, 1);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<Integer> getStashIDsByItemAndPrefix(final String itemID, final String prefixID, final boolean isHardcore, final String charName) {
        List<Integer> list = new LinkedList<Integer>();
        if (prefixID == null) {
            return list;
        }
        String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND PREFIX_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (!GDStashFrame.iniConfig.sectRestrict.transferSoulbound && charName != null) {
            command = command + " AND CHARNAME = '" + charName + "'";
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            ps.setString(2, prefixID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapInteger(rs, 1);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<Integer> getStashIDsByItemAndSuffix(final String itemID, final String suffixID, final boolean isHardcore, final String charName) {
        List<Integer> list = new LinkedList<Integer>();
        if (suffixID == null) {
            return list;
        }
        String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_ID = ? AND SUFFIX_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (!GDStashFrame.iniConfig.sectRestrict.transferSoulbound && charName != null) {
            command = command + " AND CHARNAME = '" + charName + "'";
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            ps.setString(2, suffixID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapInteger(rs, 1);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<Integer> getStashIDsByClassAndPrefixSuffix(final String itemClassID, final String prefixID, final String suffixID, final boolean isHardcore, final String charName) {
        List<Integer> list = new LinkedList<Integer>();
        if (prefixID == null && suffixID == null) {
            return list;
        }
        String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_CLASS = ? AND PREFIX_ID = ? AND SUFFIX_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (!GDStashFrame.iniConfig.sectRestrict.transferSoulbound && charName != null) {
            command = command + " AND CHARNAME = '" + charName + "'";
        }
        String pre = "";
        if (prefixID != null) {
            pre = prefixID;
        }
        String suf = "";
        if (suffixID != null) {
            suf = suffixID;
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemClassID);
            ps.setString(2, pre);
            ps.setString(3, suf);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapInteger(rs, 1);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemClassID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<Integer> getStashIDsByClassAndPrefix(final String itemClassID, final String prefixID, final boolean isHardcore, final String charName) {
        List<Integer> list = new LinkedList<Integer>();
        if (prefixID == null) {
            return list;
        }
        String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_CLASS = ? AND PREFIX_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (!GDStashFrame.iniConfig.sectRestrict.transferSoulbound && charName != null) {
            command = command + " AND CHARNAME = '" + charName + "'";
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemClassID);
            ps.setString(2, prefixID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapInteger(rs, 1);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemClassID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<Integer> getStashIDsByClassAndSuffix(final String itemClassID, final String suffixID, final boolean isHardcore, final String charName) {
        List<Integer> list = new LinkedList<Integer>();
        if (suffixID == null) {
            return list;
        }
        String command = "SELECT STASH_ID FROM STASH_ITEM_V10 WHERE ITEM_CLASS = ? AND SUFFIX_ID = ?";
        if (!GDStashFrame.iniConfig.sectRestrict.transferSCHC) {
            if (isHardcore) {
                command += " AND HARDCORE = true";
            } else {
                command += " AND HARDCORE = false";
            }
        }
        if (!GDStashFrame.iniConfig.sectRestrict.transferSoulbound && charName != null) {
            command = command + " AND CHARNAME = '" + charName + "'";
        }
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemClassID);
            ps.setString(2, suffixID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapInteger(rs, 1);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemClassID, "STASH_ITEM_V10"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<GDItem> loadGDS(final File file, final Charset cs) {
        List<GDItem> items = new LinkedList<GDItem>();
        String filename = null;
        try {
            filename = file.getCanonicalPath();
        } catch (IOException ex) {
            filename = null;
        }
        try (final InputStream reader = new BufferedInputStream(new FileInputStream(file))) {
            final int version = GDReader.readInt(reader);
            for (int size = GDReader.readInt(reader), i = 0; i < size; ++i) {
                final DBStashItem si = new DBStashItem();
                final boolean read = si.readGDS(reader, filename, cs, version);
                if (read) {
                    final GDItem item = new GDItem(si, filename);
                    items.add(item);
                }
            }
        } catch (IOException ex) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_LOAD_FILE"));
            GDMsgLogger.addError(ex);
            items = null;
        }
        return items;
    }

    private static boolean writeDBStashItemListGDS(final File file, final Charset cs, final List<DBStashItem> items) {
        boolean success = false;
        try {
            file.createNewFile();
            try (final FileOutputStream writer = new FileOutputStream(file)) {
                writer.write(GDWriter.intToBytes4(2));
                final int size = items.size();
                writer.write(GDWriter.intToBytes4(size));
                for (final DBStashItem item : items) {
                    item.writeGDS(writer, cs);
                }
                writer.flush();
                success = true;
            } catch (IOException ex) {
                throw ex;
            }
        } catch (IOException ex) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_WRITE_FILE"));
            GDMsgLogger.addError(ex);
        }
        return success;
    }

    public static List<GDItem> loadIAS(final File file) {
        List<GDItem> items = new LinkedList<GDItem>();
        String filename = null;
        try {
            filename = file.getCanonicalPath();
        } catch (IOException ex2) {
            filename = null;
        }
        try (final InputStream reader = new BufferedInputStream(new FileInputStream(file))) {
            final int version = GDReader.readShort(reader);
            if (version < 1 || version > 4) {
                throw new FileVersionException();
            }
            for (int size = GDReader.readInt(reader), i = 0; i < size; ++i) {
                final DBStashItem si = new DBStashItem();
                final boolean read = si.readIAS(reader, version, filename);
                if (read) {
                    final GDItem item = new GDItem(si, filename);
                    items.add(item);
                }
            }
        } catch (FileVersionException ex) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            GDMsgLogger.addError(ex);
            items = null;
        } catch (IOException ex2) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_LOAD_FILE"));
            GDMsgLogger.addError(ex2);
            items = null;
        }
        return items;
    }

    public DBStashItem clone() {
        final DBStashItem item = new DBStashItem(this);
        return item;
    }

    public int getStashID() {
        return this.stashID;
    }

    public void setStashID(final int stashID) {
        this.stashID = stashID;
    }

    public String getItemID() {
        return this.itemID;
    }

    public void setItemID(final String itemID) {
        this.itemID = itemID;
    }

    public String getPrefixID() {
        return this.prefixID;
    }

    public void setPrefixID(final String prefixID) {
        this.prefixID = prefixID;
    }

    public String getSuffixID() {
        return this.suffixID;
    }

    public void setSuffixID(final String suffixID) {
        this.suffixID = suffixID;
    }

    public String getModifierID() {
        return this.modifierID;
    }

    public void setModifierID(final String modifierID) {
        this.modifierID = modifierID;
    }

    public String getTransmuteID() {
        return this.transmuteID;
    }

    public void setTransmuteID(final String transmuteID) {
        this.transmuteID = transmuteID;
    }

    public int getItemSeed() {
        return this.seed;
    }

    public void setItemSeed(final int seed) {
        this.seed = seed;
    }

    public String getComponentID() {
        return this.relicID;
    }

    public void setComponentID(final String relicID) {
        this.relicID = relicID;
    }

    public String getCompletionBonusID() {
        return this.relicBonusID;
    }

    public void setCompletionBonusID(final String relicBonusID) {
        this.relicBonusID = relicBonusID;
    }

    public int getComponentSeed() {
        return this.relicSeed;
    }

    public void setComponentSeed(final int relicSeed) {
        this.relicSeed = relicSeed;
    }

    public String getAugmentID() {
        return this.enchantmentID;
    }

    public void setAugmentID(final String enchantmentID) {
        this.enchantmentID = enchantmentID;
    }

    public int getAugmentLevel() {
        return this.enchantmentLevel;
    }

    public void setAugmentLevel(final int enchantmentLevel) {
        this.enchantmentLevel = enchantmentLevel;
    }

    public int getAugmentSeed() {
        return this.enchantmentSeed;
    }

    public void setAugmentSeed(final int enchantmentSeed) {
        this.enchantmentSeed = enchantmentSeed;
    }

    public String getAscendantID() {
        return this.ascendantID;
    }

    public void setAscendantID(final String ascendantID) {
        this.ascendantID = ascendantID;
    }

    public String getAscendant2hID() {
        return this.ascendant2hID;
    }

    public void setAscendant2hID(final String ascendant2hID) {
        this.ascendant2hID = ascendant2hID;
    }

    public int getVar1() {
        return this.var1;
    }

    public void setVar1(final int var1) {
        this.var1 = var1;
    }

    public int getStackCount() {
        return this.stackCount;
    }

    private static String determineBetterRarity(final String rarity1, final String rarity2) {
        if (rarity1.equals("Quest")) {
            return rarity1;
        }
        if (rarity2.equals("Quest")) {
            return rarity2;
        }
        String rarity3 = rarity1;
        if (rarity3.equals("Legendary")) {
            return rarity3;
        }
        if (rarity3.equals("Epic") && rarity2.equals("Legendary")) {
            rarity3 = rarity2;
        }
        if (rarity3.equals("Rare") && (rarity2.equals("Legendary") || rarity2.equals("Epic"))) {
            rarity3 = rarity2;
        }
        if (rarity3.equals("Magical") && (rarity2.equals("Legendary") || rarity2.equals("Epic") || rarity2.equals("Rare"))) {
            rarity3 = rarity2;
        }
        if (rarity3.equals("Common") && (rarity2.equals("Legendary") || rarity2.equals("Epic") || rarity2.equals("Rare") || rarity2.equals("Magical"))) {
            rarity3 = rarity2;
        }
        return rarity3;
    }

    public void setStackCount(final int stackCount) {
        this.stackCount = stackCount;
    }

    public int getRerollsUsed() {
        return this.rerollsUsed;
    }

    public void setRerollsUsed(final int rerollsUsed) {
        this.rerollsUsed = rerollsUsed;
    }

    public boolean isHardcore() {
        return this.hardcore;
    }

    public void setHardcore(final boolean hardcore) {
        this.hardcore = hardcore;
    }

    public String getCharName() {
        return this.charname;
    }

    public void setCharName(final String charname) {
        this.charname = charname;
    }

    public boolean isSoulbound() {
        return this.soulbound;
    }

    public void setSoulbound(final boolean soulbound) {
        this.soulbound = soulbound;
    }

    public String getRarity() {
        return this.rarity;
    }

    public int getRequiredlevel() {
        return this.reqLevel;
    }

    public int getItemSkillLevel() {
        if (this.dbItem == null) {
            return 1;
        }
        return this.dbItem.getItemSkillLevel();
    }

    public int getRequiredCunning() {
        return this.reqDex;
    }

    public static void storeGDItems(final List<GDItem> items) {
        if (items == null) {
            return;
        }
        int iFound = 0;
        int iInsert = 0;
        for (final GDItem item : items) {
            boolean found = false;
            try {
                found = isStored(item.getStashItem());
            } catch (SQLException ex) {
                final Object[] args = {item.getItemID()};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_SEARCH", args);
                GDMsgLogger.addError(msg);
                GDMsgLogger.addError(ex);
                continue;
            }
            if (found) {
                ++iFound;
            } else {
                try {
                    insert(item.getStashItem());
                    ++iInsert;
                } catch (SQLException ex) {
                    final Object[] args = {item.getItemID()};
                    final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_STORE", args);
                    GDMsgLogger.addError(msg);
                    GDMsgLogger.addError(ex);
                }
            }
        }
        final Object[] args2 = {iInsert};
        String msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_STORED_NUM", args2);
        GDMsgLogger.addSuccess(msg2);
        final Object[] args3 = {iFound};
        msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_FOUND_NUM", args3);
        GDMsgLogger.addWarning(msg2);
    }

    public int getRequiredPhysique() {
        return this.reqStr;
    }

    public static boolean storeItem(final GDItem item) {
        return storeItem(item.getStashItem());
    }

    public static boolean storeItem(final DBStashItem item) {
        boolean success = false;
        boolean found = false;
        try {
            found = isStored(item);
        } catch (SQLException ex) {
            final Object[] args = {item.itemID};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_STORE", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex);
            return false;
        }
        if (found) {
            final Object[] args2 = {item.itemID};
            final String msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_IN_STORE", args2);
            GDMsgLogger.addWarning(msg2);
            return false;
        }
        try {
            insert(item);
            success = true;
        } catch (SQLException ex) {
            final Object[] args = {item.itemID};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_STASH_ITEM_STORE", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex);
        }
        return success;
    }

    private static void insert(final DBStashItem item) throws SQLException {
        if (item.isStackable()) {
            insertStack(item);
        } else {
            insertNonStack(item);
        }
    }

    private static void insertStack(final DBStashItem item) throws SQLException {
        final DBStashItem si = getStashItemForStack(item);
        if (si == null) {
            insertNonStack(item);
        } else {
            updateStackCount(si.stashID, si.stackCount + item.stackCount);
        }
    }

    private static void updateStackCount(final int stashID, final int stackSize) throws SQLException {
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try {
                updateStackCount(conn, stashID, stackSize);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    public int getRequiredSpirit() {
        return this.reqInt;
    }

    private static void insertNonStack(final DBStashItem item) throws SQLException {
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try {
                insertNonStack(conn, item);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    public DBItem getDBItem() {
        return this.dbItem;
    }

    public static boolean isStored(final GDItem item) throws SQLException {
        return isStored(item.getStashItem());
    }

    private static boolean isStored(final DBStashItem item) throws SQLException {
        return !item.isStackable() && isStoredNonStack(item);
    }

    public DBAffix getDBPrefix() {
        return this.dbPrefix;
    }

    public DBAffix getDBSuffix() {
        return this.dbSuffix;
    }

    public DBAffix getDBModifier() {
        return this.dbModifier;
    }

    public DBItem getDBComponent() {
        return this.dbComponent;
    }

    public static boolean delete(final GDItem item) {
        return delete(item.getStashItem());
    }

    private static boolean delete(final DBStashItem item) {
        if (item.isStackable()) {
            return deleteStack(item);
        }
        return deleteNonStack(item);
    }

    private static boolean deleteStack(final DBStashItem item) {
        DBStashItem si = null;
        try {
            si = getStashItemForStack(item);
        } catch (SQLException ex) {
            return false;
        }
        if (si == null) {
            return false;
        }
        if (si.stackCount <= item.stackCount) {
            deleteStackDB(si);
        } else {
            try {
                updateStackCount(si.stashID, si.stackCount - item.stackCount);
            } catch (SQLException ex) {
                return false;
            }
        }
        return true;
    }

    public DBAffix getDBCompletionBonus() {
        return this.dbBonus;
    }

    public DBItem getDBAugment() {
        return this.dbEnchantment;
    }

    public static void updateDependentFields(final List<DBStashItem> items) {
        if (items == null) {
            return;
        }
        int iFound = 0;
        int iMissing = 0;
        int iRemoved = 0;
        int iError = 0;
        for (final DBStashItem item : items) {
            if (item.stashID == -1) {
                ++iMissing;
            } else {
                try {
                    if (item.getDBItem() == null) {
                        ++iRemoved;
                        deleteItemByStashID(item);
                    } else {
                        updateDependentFields(item);
                        ++iFound;
                    }
                } catch (SQLException ex) {
                    ++iError;
                }
            }
        }
        if (iFound > 0) {
            final Object[] args = {iFound};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_UPD_FOUND", args);
            GDMsgLogger.addInfo(msg);
        }
        if (iMissing > 0) {
            final Object[] args = {iMissing};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_UPD_MISS", args);
            GDMsgLogger.addWarning(msg);
        }
        if (iRemoved > 0) {
            final Object[] args = {iRemoved};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_DEL", args);
            GDMsgLogger.addWarning(msg);
        }
        if (iError > 0) {
            final Object[] args = {iError};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_UPD_ERROR", args);
            GDMsgLogger.addError(msg);
        }
    }

    public boolean isComponent() {
        return this.dbItem != null && this.dbItem.isComponent();
    }

    public boolean isIncompleteComponent() {
        if (this.dbItem == null) {
            return true;
        }
        if (this.dbItem.isComponent()) {
            return this.getVar1() != this.dbItem.getComponentPieces();
        }
        return this.dbComponent == null || this.getVar1() != this.dbComponent.getComponentPieces();
    }

    public static void convertStash(final String tableName, final List<DBStashItem> items) {
        if (items == null) {
            return;
        }
        int iFound = 0;
        int iMissing = 0;
        int iRemoved = 0;
        int iError = 0;
        for (final DBStashItem item : items) {
            if (item.stashID == -1) {
                ++iMissing;
            } else {
                if (item.getDBItem() == null) {
                    ++iRemoved;
                }
                try {
                    convertStash(tableName, item);
                    ++iFound;
                } catch (SQLException ex) {
                    ++iError;
                }
            }
        }
        if (iFound > 0) {
            final Object[] args = {iFound};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_FOUND", args);
            GDMsgLogger.addInfo(msg);
        }
        if (iMissing > 0) {
            final Object[] args = {iMissing};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_MISS", args);
            GDMsgLogger.addWarning(msg);
        }
        if (iRemoved > 0) {
            final Object[] args = {iRemoved};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_DEL", args);
            GDMsgLogger.addWarning(msg);
        }
        if (iError > 0) {
            final Object[] args = {iError};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_ERROR", args);
            GDMsgLogger.addError(msg);
        }
    }

    private static void convertStash(final String tableName, final DBStashItem item) throws SQLException {
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try {
                if (item.getDBItem() != null) {
                    insert(conn, item);
                }
                deleteOld(conn, tableName, item);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    public static void moveStash(final Connection connOld, final Connection connNew, final String tableName, final List<DBStashItem> items) {
        if (items == null) {
            return;
        }
        int iFound = 0;
        int iMissing = 0;
        int iRemoved = 0;
        int iError = 0;
        for (final DBStashItem item : items) {
            if (item.stashID == -1) {
                ++iMissing;
            } else {
                if (item.getDBItem() == null) {
                    ++iRemoved;
                }
                try {
                    moveStash(connOld, connNew, tableName, item);
                    ++iFound;
                } catch (SQLException ex) {
                    ++iError;
                }
            }
        }
        if (iFound > 0) {
            final Object[] args = {iFound};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_FOUND", args);
            GDMsgLogger.addInfo(msg);
        }
        if (iMissing > 0) {
            final Object[] args = {iMissing};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_MISS", args);
            GDMsgLogger.addWarning(msg);
        }
        if (iRemoved > 0) {
            final Object[] args = {iRemoved};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_DEL", args);
            GDMsgLogger.addWarning(msg);
        }
        if (iError > 0) {
            final Object[] args = {iError};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "INFO_STASH_CONV_ERROR", args);
            GDMsgLogger.addError(msg);
        }
    }

    private static void moveStash(final Connection connOld, final Connection connNew, final String tableName, final DBStashItem item) throws SQLException {
        try {
            final boolean autoOld = connOld.getAutoCommit();
            connOld.setAutoCommit(false);
            final boolean autoNew = connNew.getAutoCommit();
            connNew.setAutoCommit(false);
            try {
                if (item.getDBItem() != null) {
                    insert(connNew, item);
                }
                deleteOld(connOld, tableName, item);
                connNew.commit();
                connOld.commit();
            } catch (SQLException ex) {
                connNew.rollback();
                connOld.rollback();
                throw ex;
            } finally {
                connOld.setAutoCommit(autoOld);
                connNew.setAutoCommit(autoNew);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    private static void insert(final Connection conn, final DBStashItem item) throws SQLException {
        if (item.isStackable()) {
            insertStack(conn, item);
        } else {
            insertNonStack(conn, item);
        }
    }

    private static void insertStack(final Connection conn, final DBStashItem item) throws SQLException {
        final DBStashItem si = getStashItemForStack(item);
        if (si == null) {
            insertNonStack(conn, item);
        } else {
            updateStackCount(conn, si.stashID, si.stackCount + item.stackCount);
        }
    }

    private static void deleteOld(final Connection conn, final String tableName, final DBStashItem item) throws SQLException {
        if (item == null) {
            return;
        }
        if (item.stashID == -1) {
            return;
        }
        final String command = "DELETE FROM " + tableName + " WHERE STASH_ID = ?";
        try (final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setInt(1, item.stashID);
            ps.executeUpdate();
            ps.close();
        } catch (SQLException ex) {
            throw ex;
        }
    }

    public static void dropOldStash(final Connection conn, final String tableName) {
        final String dropTable = "DROP TABLE " + tableName;
        try {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, tableName)) {
                    final List<DBStashItem> items = getAllOld(conn, tableName);
                    if (items.isEmpty()) {
                        st.execute(dropTable);
                    } else {
                        final Object[] args = {tableName};
                        final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "TABLE_CONTAINS_ENTRIES", args);
                        GDMsgLogger.addError(msg);
                    }
                }
                st.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args2 = {tableName};
                final String msg2 = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_DROP_TABLE", args2);
                GDMsgLogger.addError(msg2);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
        }
    }

    public static GDItem getStack(final GDItem item) {
        final DBStashItem si = getStack(item.getStashItem());
        if (si == null) {
            return null;
        }
        return new GDItem(si);
    }

    public int getComponentPieces() {
        if (this.dbItem == null) {
            return 0;
        }
        if (this.dbItem.isComponent()) {
            return this.dbItem.getComponentPieces();
        }
        if (this.dbComponent == null) {
            return 0;
        }
        return this.dbComponent.getComponentPieces();
    }

    public boolean isStackable() {
        if (this.dbItem == null) {
            return false;
        }
        if (this.dbItem.getItemClass().equals("ItemRelic")) {
            return this.getVar1() >= this.dbItem.getComponentPieces();
        }
        return this.dbItem.isStackable();
    }

    public boolean isEpic() {
        return this.dbItem != null && this.dbItem.isEpic();
    }

    public boolean isLegendary() {
        return this.dbItem != null && this.dbItem.isLegendary();
    }

    public boolean isUnique() {
        return this.dbItem != null && this.dbItem.isUnique();
    }

    public int getDefaultStackSize() {
        if (this.dbItem == null) {
            return 1;
        }
        int size = this.dbItem.getMaxStackSize();
        if (size == 0) {
            size = 1;
            final String itemClass = this.dbItem.getItemClass();
            size = ItemClass.getDefaultStackSize(itemClass);
            if (itemClass.equals("QuestItem") && DBItem.QUEST_ITEMS.contains(this.itemID)) {
                size = 100;
            }
        }
        if (size > this.stackCount) {
            size = this.stackCount;
        }
        return size;
    }

    public void fillDependentStats(final GDLog log) {
        this.soulbound = false;
        this.rarity = null;
        this.reqLevel = 0;
        this.reqDex = 0;
        this.reqInt = 0;
        this.reqStr = 0;
        this.itemClass = null;
        this.armorClass = null;
        this.artifactClass = null;
        this.itemLevel = 0;
        this.setID = null;
        this.itemName = null;
        this.petBonusSkillID = null;
        this.plusAllSkills = 0;
        this.itemSkillID = null;
        this.convertIn = null;
        this.convertOut = null;
        this.convertIn2 = null;
        this.convertOut2 = null;
        this.enemyOnly = false;
        if (this.slots != null) {
            this.slots.clear();
        }
        this.dbItem = null;
        this.dbPrefix = null;
        this.dbSuffix = null;
        this.dbModifier = null;
        this.dbComponent = null;
        this.dbBonus = null;
        this.dbEnchantment = null;
        if (this.getStackCount() == 0) {
            this.setStackCount(1);
        }
        if (this.getItemID() == null) {
            return;
        }
        this.dbItem = DBItem.get(this.getItemID());
        if (this.dbItem == null) {
            final Object[] args = {"", 0, this.getItemID()};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_ITEM_NOT_FOUND", args);
            if (log == null) {
                GDMsgLogger.addWarning(msg);
            } else {
                log.addWarning(msg);
            }
        }
        if (this.dbItem == null) {
            return;
        }
        if (this.getPrefixID() != null) {
            this.dbPrefix = DBAffix.get(this.getPrefixID());
            if (this.dbPrefix == null) {
                final Object[] args = {"", 0, this.dbItem.getItemName(), this.getPrefixID()};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_AFFIX_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addWarning(msg);
                } else {
                    log.addWarning(msg);
                }
            }
        }
        if (this.getSuffixID() != null) {
            this.dbSuffix = DBAffix.get(this.getSuffixID());
            if (this.dbSuffix == null) {
                final Object[] args = {"", 0, this.dbItem.getItemName(), this.getSuffixID()};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_AFFIX_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addWarning(msg);
                } else {
                    log.addWarning(msg);
                }
            }
        }
        if (this.getModifierID() != null) {
            this.dbModifier = DBAffix.get(this.getModifierID());
            if (this.dbModifier == null) {
                final Object[] args = {"", 0, this.dbItem.getItemName(), this.getModifierID()};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_AFFIX_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addWarning(msg);
                } else {
                    log.addWarning(msg);
                }
            }
        }
        if (this.getComponentID() != null) {
            this.dbComponent = DBItem.get(this.getComponentID());
            if (this.dbComponent == null) {
                final Object[] args = {"", 0, this.getComponentID()};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_ITEM_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addWarning(msg);
                } else {
                    log.addWarning(msg);
                }
            }
        }
        if (this.getCompletionBonusID() != null) {
            this.dbBonus = DBAffix.get(this.getCompletionBonusID());
            if (this.dbBonus == null) {
                final Object[] args = {"", 0, this.dbItem.getItemName(), this.getCompletionBonusID()};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_AFFIX_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addWarning(msg);
                } else {
                    log.addWarning(msg);
                }
            }
        }
        if (this.getAugmentID() != null) {
            this.dbEnchantment = DBItem.get(this.getAugmentID());
            if (this.dbEnchantment == null) {
                final Object[] args = {"", 0, this.dbItem.getItemName(), this.getAugmentID()};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_AUGMENT_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addWarning(msg);
                } else {
                    log.addWarning(msg);
                }
            }
        }
        final DBFormulaSet.Result result = this.dbItem.getStatRequirements(this);
        if (result != null) {
            this.reqDex = (int) result.cunning;
            this.reqInt = (int) result.spirit;
            this.reqStr = (int) result.physique;
        }
        if (this.dbItem.isComponent()) {
            if (this.getVar1() > this.dbItem.getComponentPieces()) {
                this.setVar1(this.dbItem.getComponentPieces());
            }
            if (this.getCompletionBonusID() != null) {
                this.setCompletionBonusID(null);
            }
        }
        this.soulbound = this.dbItem.isSoulbound();
        if (this.dbComponent != null && this.dbComponent.isSoulbound()) {
            this.soulbound = true;
        }
        if (this.dbEnchantment != null && this.dbEnchantment.isSoulbound()) {
            this.soulbound = true;
        }
        if (this.charname == null) {
            this.soulbound = false;
        }
        this.rarity = this.determineRarity();
        this.reqLevel = this.determineRequiredLevel();
        this.itemClass = this.dbItem.getItemClass();
        this.armorClass = this.dbItem.getArmorClass();
        this.artifactClass = this.dbItem.getArtifactClass();
        this.itemLevel = this.dbItem.getItemLevel();
        this.setID = this.dbItem.getItemSetID();
        this.itemName = this.getCompleteName(false, false).toUpperCase(GDMsgFormatter.locale);
        this.petBonusSkillID = this.dbItem.getPetBonusSkillID();
        this.plusAllSkills = this.dbItem.getPlusAllSkills();
        this.itemSkillID = this.dbItem.getItemSkillID();
        this.convertIn = this.dbItem.getConvertIn();
        this.convertOut = this.dbItem.getConvertOut();
        this.convertIn2 = this.dbItem.getConvertIn2();
        this.convertOut2 = this.dbItem.getConvertOut2();
        this.enemyOnly = this.dbItem.isEnemyOnly();
        this.slots = this.dbItem.getSlots();
        if (!this.soulbound && !this.dbItem.isQuestItem()) {
            this.charname = null;
        }
    }

    private String appendNamePart(String s, final int part, final boolean withDmg) {
        if (this.dbItem == null) {
            return s;
        }
        String sPart = null;
        switch (part) {
            case 1: {
                if (this.dbPrefix == null || this.dbItem.isHidePrefix()) {
                    break;
                }
                sPart = this.dbPrefix.getGenderText(this.dbItem.getGenderCode());
                if (sPart != null) {
                    if (!s.isEmpty()) {
                        s += " ";
                    }
                    s += sPart;
                    break;
                }
                break;
            }
            case 2: {
                sPart = this.dbItem.getQualityText();
                if (sPart != null) {
                    if (!s.isEmpty()) {
                        s += " ";
                    }
                    s += sPart;
                    break;
                }
                break;
            }
            case 3: {
                sPart = this.dbItem.getStyleText();
                if (sPart != null) {
                    if (!s.isEmpty()) {
                        s += " ";
                    }
                    s += sPart;
                    break;
                }
                break;
            }
            case 4: {
                sPart = this.dbItem.getName(false);
                if (sPart == null) {
                    break;
                }
                if (!s.isEmpty()) {
                    s += " ";
                }
                s += sPart;
                if (withDmg) {
                    final String dmg = this.dbItem.getMainDamageType();
                    if (dmg != null) {
                        s = s + " [" + dmg + "]";
                    }
                    break;
                }
                break;
            }
            case 5: {
                if (this.dbSuffix == null || this.dbItem.isHideSuffix()) {
                    break;
                }
                sPart = this.dbSuffix.getGenderText(this.dbItem.getGenderCode());
                if (sPart != null) {
                    if (!s.isEmpty()) {
                        s += " ";
                    }
                    s += sPart;
                    break;
                }
                break;
            }
        }
        return s;
    }

    public static List<GDItem> getGDItemsByItemIDs(final List<String> itemIDs, final boolean isHarcore) {
        final List<GDItem> list = new LinkedList<GDItem>();
        for (final String itemID : itemIDs) {
            final List<GDItem> l = getGDItemsByItemID(itemID, isHarcore);
            if (!l.isEmpty()) {
                list.addAll(l);
            }
        }
        return list;
    }

    public String getCompleteName(final boolean inclFaction, final boolean inclMod) {
        if (this.dbItem == null) {
            return null;
        }
        String name = "";
        name = this.appendNamePart(name, GDStashFrame.dbConfig.namePart1, false);
        name = this.appendNamePart(name, GDStashFrame.dbConfig.namePart2, false);
        name = this.appendNamePart(name, GDStashFrame.dbConfig.namePart3, false);
        name = this.appendNamePart(name, GDStashFrame.dbConfig.namePart4, false);
        name = this.appendNamePart(name, GDStashFrame.dbConfig.namePart5, false);
        if (inclFaction && this.dbItem.getFaction() != null) {
            name = name + " (" + this.dbItem.getFaction() + ")";
        }
        if (inclMod) {
            final String modName = this.dbItem.getModName();
            if (modName != null) {
                name = name + " " + modName;
            }
        }
        return name;
    }

    public static List<GDItem> getGDItemsByItemIDs(final List<String> itemIDs) {
        final List<GDItem> list = new LinkedList<GDItem>();
        for (final String itemID : itemIDs) {
            final List<GDItem> l = getGDItemsByItemID(itemID);
            if (!l.isEmpty()) {
                list.addAll(l);
            }
        }
        return list;
    }

    private static List<GDItem> getGDItemsByStashIDs(final List<Integer> stashIDs, final CriteriaCombination.Soulbound soulbound, final CriteriaCombination.SC_HC schc, final boolean isHardcore, final String charName) {
        final List<GDItem> list = new LinkedList<GDItem>();
        if (stashIDs == null) {
            return list;
        }
        for (final int stashID : stashIDs) {
            final DBStashItem si = get(stashID, soulbound, schc, isHardcore, charName);
            if (si != null) {
                final GDItem item = new GDItem(si);
                list.add(item);
            }
        }
        return list;
    }

    private static List<DBStashItem> wrap(final ResultSet rs) throws SQLException {
        final LinkedList<DBStashItem> list = new LinkedList<DBStashItem>();
        while (rs.next()) {
            try {
                final DBStashItem item = wrapSingle(rs);
                list.add(item);
            } catch (SQLException ex) {
            }
        }
        return list;
    }

    public String determineRarity() {
        String itemRarity = "Common";
        String prefixRarity = "Common";
        String suffixRarity = "Common";
        if (this.dbItem == null) {
            return "Broken";
        }
        if (this.dbItem.getRarity() != null) {
            itemRarity = this.dbItem.getRarity();
        }
        if (this.dbPrefix != null && this.dbPrefix.getRarity() != null) {
            prefixRarity = this.dbPrefix.getRarity();
        }
        if (this.dbSuffix != null && this.dbSuffix.getRarity() != null) {
            suffixRarity = this.dbSuffix.getRarity();
        }
        String rarity = null;
        rarity = determineBetterRarity(prefixRarity, suffixRarity);
        rarity = determineBetterRarity(itemRarity, rarity);
        return rarity;
    }

    private static List<GDItem> wrapGDItem(final ResultSet rs) throws SQLException {
        GDItem item = null;
        final LinkedList<GDItem> list = new LinkedList<GDItem>();
        while (rs.next()) {
            try {
                final DBStashItem si = wrapSingle(rs);
                item = new GDItem(si);
                list.add(item);
            } catch (SQLException ex) {
            }
        }
        return list;
    }

    public int determineRequiredLevel() {
        int level = 0;
        if (this.dbItem == null) {
            return 0;
        }
        if (this.dbItem != null && this.dbItem.getRequiredlevel() >= level) {
            level = this.dbItem.getRequiredlevel();
        }
        if (this.dbPrefix != null && this.dbPrefix.getRequiredlevel() >= level) {
            level = this.dbPrefix.getRequiredlevel();
        }
        if (this.dbSuffix != null && this.dbSuffix.getRequiredlevel() >= level) {
            level = this.dbSuffix.getRequiredlevel();
        }
        if (this.dbModifier != null && this.dbModifier.getRequiredlevel() >= level) {
            level = this.dbModifier.getRequiredlevel();
        }
        if (this.dbComponent != null && this.dbComponent.getRequiredlevel() >= level) {
            level = this.dbComponent.getRequiredlevel();
        }
        if (this.dbBonus != null && this.dbBonus.getRequiredlevel() >= level) {
            level = this.dbBonus.getRequiredlevel();
        }
        if (this.dbEnchantment != null && this.dbEnchantment.getRequiredlevel() >= level) {
            level = this.dbEnchantment.getRequiredlevel();
        }
        return level;
    }

    @Override
    public String getItemClass() {
        if (this.dbItem == null) {
            return null;
        }
        return this.dbItem.getItemClass();
    }

    private static List<Integer> getStashIDsByItemIDs(final List<String> itemIDs, final boolean isHardcore, final String charName) {
        final List<Integer> listAll = new LinkedList<Integer>();
        for (final String itemID : itemIDs) {
            final List<Integer> list = getStashIDsByItemID(itemID, isHardcore, charName);
            if (list != null) {
                listAll.addAll(list);
            }
        }
        return listAll;
    }

    @Override
    public float getCharAttackSpeed() {
        if (this.dbItem == null) {
            return 0.0f;
        }
        return this.dbItem.getCharAttackSpeed();
    }

    private static void mergeStashIDs(final List<Integer> listAll, final List<Integer> list) {
        if (list == null) {
            return;
        }
        for (final Integer sInt : list) {
            final int stashID = sInt;
            boolean found = false;
            for (final int sid : listAll) {
                if (sid == stashID) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                listAll.add(sInt);
            }
        }
    }

    private static List<Integer> getByCriteria(final SelectionCriteria criteria, final CriteriaCombination.Soulbound soulbound, final CriteriaCombination.SC_HC schc, final boolean isHardcore, final String charName) {
        final List<Integer> itemsAll = new LinkedList<Integer>();
        List<Integer> items = null;
        List<Integer> itemSets = null;
        List<Integer> prefixes = null;
        List<Integer> suffixes = null;
        items = StashIDItemCombination.getStashIDs(criteria, soulbound, schc, isHardcore, charName);
        itemSets = StashIDItemSetCombination.getStashIDs(criteria, soulbound, schc, isHardcore, charName);
        if (criteria.prefixID == null) {
            prefixes = StashIDPrefixCombination.getStashIDs(criteria, soulbound, schc, isHardcore, charName);
        }
        if (criteria.suffixID == null) {
            suffixes = StashIDSuffixCombination.getStashIDs(criteria, soulbound, schc, isHardcore, charName);
        }
        mergeStashIDs(itemsAll, items);
        mergeStashIDs(itemsAll, itemSets);
        mergeStashIDs(itemsAll, prefixes);
        mergeStashIDs(itemsAll, suffixes);
        return itemsAll;
    }

    public static List<GDItem> getGDItemByCriteria(final SelectionCriteria criteria, final CriteriaCombination.Soulbound soulbound, final CriteriaCombination.SC_HC schc, final boolean isHardcore, final String charName) {
        List<Integer> stashIDs = null;
        if (criteria.itemIDs != null && !criteria.itemIDs.isEmpty()) {
            stashIDs = getStashIDsByItemIDs(criteria.itemIDs, isHardcore, charName);
        } else {
            stashIDs = getByCriteria(criteria, soulbound, schc, isHardcore, charName);
        }
        return getGDItemsByStashIDs(stashIDs, soulbound, schc, isHardcore, charName);
    }

    public static DuplicateInfo determineSimilarItems(final GDItem item, final boolean isHardcore, final String charName) {
        if (item == null) {
            return null;
        }
        String cname = null;
        if (item.getDBItem() != null && item.getDBItem().isSoulbound()) {
            cname = charName;
        }
        final DuplicateInfo info = new DuplicateInfo();
        List<Integer> list = null;
        if (item.getRarity().equals("Epic") || item.getRarity().equals("Legendary")) {
            info.numItemCombo.background = ItemClass.getRarityBackgroundColor(item.getDBItem().getRarity());
            if (item.getPrefixID() == null && item.getSuffixID() == null) {
                list = getStashIDsByItemID(item.getItemID(), isHardcore, cname);
                info.numItemCombo.text = Integer.toString(list.size());
            } else {
                list = getStashIDsByItemAndPrefixSuffix(item.getItemID(), item.getPrefixID(), item.getSuffixID(), isHardcore, cname);
                info.numItemCombo.text = Integer.toString(list.size());
                if (item.getPrefix() != null) {
                    list = getStashIDsByItemAndPrefix(item.getItemID(), item.getPrefixID(), isHardcore, cname);
                    info.numItemPrefix.text = Integer.toString(list.size());
                    info.numItemPrefix.background = ItemClass.getRarityBackgroundColor(item.getPrefix().getRarity());
                    info.levelItemPrefix.text = Integer.toString(item.getPrefix().getRequiredlevel());
                    info.levelItemPrefix.foreground = info.numItemPrefix.foreground;
                    info.levelItemPrefix.background = info.numItemPrefix.background;
                }
                if (item.getSuffix() != null) {
                    list = getStashIDsByItemAndSuffix(item.getItemID(), item.getSuffixID(), isHardcore, cname);
                    info.numItemSuffix.text = Integer.toString(list.size());
                    info.numItemSuffix.background = ItemClass.getRarityBackgroundColor(item.getSuffix().getRarity());
                    info.levelItemSuffix.text = Integer.toString(item.getSuffix().getRequiredlevel());
                    info.levelItemSuffix.foreground = info.numItemSuffix.foreground;
                    info.levelItemSuffix.background = info.numItemSuffix.background;
                }
                list = getStashIDsByItemID(item.getItemID(), isHardcore, cname);
                info.numClassCombo.text = Integer.toString(list.size());
                info.numClassCombo.background = info.numItemCombo.background;
            }
        }
        if ((item.getRarity().equals("Magical") || item.getRarity().equals("Rare")) && (ItemClass.isArmor(item.getItemClass()) || ItemClass.isJewelry(item.getItemClass()) || ItemClass.isWeapon(item.getItemClass()))) {
            list = getStashIDsByItemAndPrefixSuffix(item.getItemID(), item.getPrefixID(), item.getSuffixID(), isHardcore, cname);
            info.numItemCombo.text = Integer.toString(list.size());
            info.numItemCombo.background = ItemClass.getRarityBackgroundColor(item.getDBItem().getRarity());
            if (item.getPrefix() != null) {
                list = getStashIDsByItemAndPrefix(item.getItemID(), item.getPrefixID(), isHardcore, cname);
                info.numItemPrefix.text = Integer.toString(list.size());
                info.numItemPrefix.background = ItemClass.getRarityBackgroundColor(item.getPrefix().getRarity());
                info.levelItemPrefix.text = Integer.toString(item.getPrefix().getRequiredlevel());
                info.levelItemPrefix.foreground = info.numItemPrefix.foreground;
                info.levelItemPrefix.background = info.numItemPrefix.background;
            }
            if (item.getSuffix() != null) {
                list = getStashIDsByItemAndSuffix(item.getItemID(), item.getSuffixID(), isHardcore, cname);
                info.numItemSuffix.text = Integer.toString(list.size());
                info.numItemSuffix.background = ItemClass.getRarityBackgroundColor(item.getSuffix().getRarity());
                info.levelItemSuffix.text = Integer.toString(item.getSuffix().getRequiredlevel());
                info.levelItemSuffix.foreground = info.numItemSuffix.foreground;
                info.levelItemSuffix.background = info.numItemSuffix.background;
            }
            list = getStashIDsByClassAndPrefixSuffix(item.getItemClass(), item.getPrefixID(), item.getSuffixID(), isHardcore, cname);
            info.numClassCombo.text = Integer.toString(list.size());
        }
        return info;
    }

    @Override
    public int getDefenseAttrArmor() {
        if (this.dbItem == null) {
            return 0;
        }
        return this.dbItem.getDefenseAttrArmor();
    }

    @Override
    public int getItemLevel() {
        if (this.dbItem == null) {
            return 0;
        }
        return this.dbItem.getItemLevel();
    }

    @Override
    public int getShieldBlockDefense() {
        if (this.dbItem == null) {
            return 0;
        }
        return this.dbItem.getShieldBlockDefense();
    }

    @Override
    public int getShieldBlockChance() {
        if (this.dbItem == null) {
            return 0;
        }
        return this.dbItem.getShieldBlockChance();
    }

    @Override
    public int getDamageAvgPierceRatio() {
        if (this.dbItem == null) {
            return 0;
        }
        return this.dbItem.getDamageAvgPierceRatio();
    }

    @Override
    public int getDamageAvgBase() {
        if (this.dbItem == null) {
            return 0;
        }
        return this.dbItem.getDamageAvgBase();
    }

    @Override
    public int getTotalAttCount() {
        if (this.dbItem == null) {
            return 0;
        }
        int total = this.dbItem.getTotalAttCount();
        if (this.dbPrefix != null) {
            total += this.dbPrefix.getTotalAttCount();
        }
        if (this.dbSuffix != null) {
            total += this.dbSuffix.getTotalAttCount();
        }
        return total;
    }

    @Override
    public int getItemPrefixCost() {
        if (this.dbPrefix == null) {
            return 0;
        }
        return this.dbPrefix.getLootRandomCost();
    }

    @Override
    public int getItemSuffixCost() {
        if (this.dbSuffix == null) {
            return 0;
        }
        return this.dbSuffix.getLootRandomCost();
    }

    public boolean readGDS(final InputStream reader, final String filename, final Charset cs, final int version) {
        boolean success = false;
        try {
            this.itemID = GDReader.readStringUByte(reader, cs);
            this.prefixID = GDReader.readStringUByte(reader, cs);
            this.suffixID = GDReader.readStringUByte(reader, cs);
            this.modifierID = GDReader.readStringUByte(reader, cs);
            this.transmuteID = GDReader.readStringUByte(reader, cs);
            this.seed = GDReader.readInt(reader);
            this.relicID = GDReader.readStringUByte(reader, cs);
            this.relicBonusID = GDReader.readStringUByte(reader, cs);
            this.relicSeed = GDReader.readInt(reader);
            this.enchantmentID = GDReader.readStringUByte(reader, cs);
            this.enchantmentLevel = GDReader.readInt(reader);
            this.enchantmentSeed = GDReader.readInt(reader);
            if (version >= 2) {
                this.ascendantID = GDReader.readStringUByte(reader, cs);
                this.ascendant2hID = GDReader.readStringUByte(reader, cs);
            }
            this.var1 = GDReader.readInt(reader);
            this.stackCount = GDReader.readInt(reader);
            if (version >= 2) {
                this.rerollsUsed = GDReader.readInt(reader);
            }
            this.hardcore = GDReader.readByteBool(reader);
            this.charname = GDReader.readStringUByte(reader, cs);
            success = true;
        } catch (IOException ex) {
            final Object[] args = {filename};
            final String s = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_FILE", args);
            GDMsgLogger.addError(s);
            GDMsgLogger.addError(ex);
            success = false;
        }
        if (success) {
            this.fillDependentStats(null);
            if (GDMsgLogger.errorsInLog()) {
                success = false;
            }
        }
        return success;
    }

    public static boolean writeAllGDS(final File file, final Charset cs) {
        boolean success = false;
        final List<DBStashItem> items = getAll();
        success = writeDBStashItemListGDS(file, cs, items);
        return success;
    }

    public static boolean writeGDStashItemListGDS(final File file, final Charset cs, final List<GDItem> list) {
        boolean success = false;
        final List<DBStashItem> items = new LinkedList<DBStashItem>();
        for (final GDItem gdi : list) {
            final DBStashItem si = gdi.getDBStashItem();
            if (si != null) {
                items.add(si);
            }
        }
        success = writeDBStashItemListGDS(file, cs, items);
        return success;
    }

    public void writeGDS(final FileOutputStream writer, final Charset cs) throws IOException {
        GDWriter.writeStringUByte(writer, this.itemID, cs);
        GDWriter.writeStringUByte(writer, this.prefixID, cs);
        GDWriter.writeStringUByte(writer, this.suffixID, cs);
        GDWriter.writeStringUByte(writer, this.modifierID, cs);
        GDWriter.writeStringUByte(writer, this.transmuteID, cs);
        writer.write(GDWriter.intToBytes4(this.seed));
        GDWriter.writeStringUByte(writer, this.relicID, cs);
        GDWriter.writeStringUByte(writer, this.relicBonusID, cs);
        writer.write(GDWriter.intToBytes4(this.relicSeed));
        GDWriter.writeStringUByte(writer, this.enchantmentID, cs);
        writer.write(GDWriter.intToBytes4(this.enchantmentLevel));
        writer.write(GDWriter.intToBytes4(this.enchantmentSeed));
        GDWriter.writeStringUByte(writer, this.ascendantID, cs);
        GDWriter.writeStringUByte(writer, this.ascendant2hID, cs);
        writer.write(GDWriter.intToBytes4(this.var1));
        writer.write(GDWriter.intToBytes4(this.stackCount));
        writer.write(GDWriter.intToBytes4(this.rerollsUsed));
        if (this.hardcore) {
            writer.write(1);
        } else {
            writer.write(0);
        }
        GDWriter.writeStringUByte(writer, this.charname, cs);
    }

    public boolean readIAS(final InputStream reader, final int version, final String filename) {
        boolean success = false;
        final int len = 0;
        try {
            this.itemID = GDReader.readStringUByte(reader);
            this.prefixID = GDReader.readStringUByte(reader);
            this.suffixID = GDReader.readStringUByte(reader);
            this.modifierID = GDReader.readStringUByte(reader);
            this.transmuteID = GDReader.readStringUByte(reader);
            this.seed = GDReader.readInt(reader);
            this.relicID = GDReader.readStringUByte(reader);
            this.relicBonusID = GDReader.readStringUByte(reader);
            this.relicSeed = GDReader.readInt(reader);
            this.enchantmentID = GDReader.readStringUByte(reader);
            this.enchantmentLevel = GDReader.readInt(reader);
            this.enchantmentSeed = GDReader.readInt(reader);
            this.var1 = GDReader.readShort(reader);
            this.stackCount = GDReader.readInt(reader);
            this.hardcore = GDReader.readByteBool(reader);
            final byte exp = GDReader.readByte(reader);
            final String modname = GDReader.readStringUByte(reader);
            if (version == 1) {
                GDReader.readLong(reader);
            }
            if (version >= 2) {
                final String azurePartition = GDReader.readStringUByte(reader);
                GDReader.readStringUByte(reader);
            }
            if (version >= 3) {
                GDReader.readByte(reader);
            }
            if (version >= 5) {
                GDReader.readStringUByte(reader);
                GDReader.readStringUByte(reader);
                GDReader.readByte(reader);
            }
            success = true;
        } catch (IOException ex) {
            final Object[] args = {filename};
            final String s = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_FILE", args);
            GDMsgLogger.addError(s);
            GDMsgLogger.addError(ex);
            success = false;
        }
        if (success) {
            this.fillDependentStats(null);
            if (GDMsgLogger.errorsInLog()) {
                success = false;
            }
        }
        return success;
    }

    public void readNewFormat(final int containerVersion) throws IOException {
        if (containerVersion != 4 && containerVersion != 5 && containerVersion != 8) {
            throw new IOException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
        }
        String s = null;
        int i = 0;
        if (containerVersion == 3) {
            i = GDReader.readEncInt(true);
            this.setStackCount(i);
        }
        s = GDReader.readEncString();
        this.setItemID(s);
        s = GDReader.readEncString();
        this.setPrefixID(s);
        s = GDReader.readEncString();
        this.setSuffixID(s);
        s = GDReader.readEncString();
        this.setModifierID(s);
        s = GDReader.readEncString();
        this.setTransmuteID(s);
        i = GDReader.readEncInt(true);
        this.setItemSeed(i);
        s = GDReader.readEncString();
        this.setComponentID(s);
        s = GDReader.readEncString();
        this.setCompletionBonusID(s);
        i = GDReader.readEncInt(true);
        this.setComponentSeed(i);
        s = GDReader.readEncString();
        this.setAugmentID(s);
        i = GDReader.readEncInt(true);
        this.setAugmentLevel(i);
        i = GDReader.readEncInt(true);
        this.setAugmentSeed(i);
        if (containerVersion >= 8) {
            s = GDReader.readEncString();
            this.setAscendantID(s);
            s = GDReader.readEncString();
            this.setAscendant2hID(s);
        }
        i = GDReader.readEncInt(true);
        this.setVar1(i);
        if (containerVersion >= 4) {
            i = GDReader.readEncInt(true);
            this.setStackCount(i);
        }
        if (containerVersion >= 8) {
            i = GDReader.readEncInt(true);
            this.setRerollsUsed(i);
        }
        this.fillDependentStats(null);
    }

    public int getByteSize(final int containerVersion) {
        int size = 0;
        String s = null;
        size += 4;
        s = this.getItemID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        s = this.getPrefixID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        s = this.getSuffixID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        s = this.getModifierID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        s = this.getTransmuteID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        size += 4;
        s = this.getComponentID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        s = this.getCompletionBonusID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        size += 4;
        s = this.getAugmentID();
        if (s != null) {
            size += s.length();
        }
        size += 4;
        size += 4;
        if (containerVersion >= 8) {
            size += 4;
            s = this.getAscendantID();
            if (s != null) {
                size += s.length();
            }
            size += 4;
            s = this.getAscendant2hID();
            if (s != null) {
                size += s.length();
            }
        }
        size += 4;
        size += 4;
        if (containerVersion >= 8) {
            size += 4;
        }
        return size;
    }

    public void write(final int containerVersion) throws IOException {
        GDWriter.writeString(this.getItemID());
        GDWriter.writeString(this.getPrefixID());
        GDWriter.writeString(this.getSuffixID());
        GDWriter.writeString(this.getModifierID());
        GDWriter.writeString(this.getTransmuteID());
        GDWriter.writeInt(this.getItemSeed());
        GDWriter.writeString(this.getComponentID());
        GDWriter.writeString(this.getCompletionBonusID());
        GDWriter.writeInt(this.getComponentSeed());
        GDWriter.writeString(this.getAugmentID());
        GDWriter.writeInt(this.getAugmentLevel());
        GDWriter.writeInt(this.getAugmentSeed());
        if (containerVersion >= 8) {
            GDWriter.writeString(this.getAscendantID());
            GDWriter.writeString(this.getAscendant2hID());
        }
        GDWriter.writeInt(this.getVar1());
        GDWriter.writeInt(this.getStackCount());
        if (containerVersion >= 8) {
            GDWriter.writeInt(this.getRerollsUsed());
        }
    }

    public static class DuplicateInfo {
        public GDItem.LabelInfo numItemCombo;
        public GDItem.LabelInfo numItemPrefix;
        public GDItem.LabelInfo levelItemPrefix;
        public GDItem.LabelInfo numItemSuffix;
        public GDItem.LabelInfo levelItemSuffix;
        public GDItem.LabelInfo numClassCombo;

        public DuplicateInfo() {
            this.numItemCombo = new GDItem.LabelInfo();
            this.numItemPrefix = new GDItem.LabelInfo();
            this.levelItemPrefix = new GDItem.LabelInfo();
            this.numItemSuffix = new GDItem.LabelInfo();
            this.levelItemSuffix = new GDItem.LabelInfo();
            this.numClassCombo = new GDItem.LabelInfo();
        }
    }
}
